/*
* v-click-outside 指令增强版
*
* 功能说明：
* 1. 支持全局点击外部区域和限定容器内点击外部区域两种模式
* 2. 支持忽略指定class的元素及其子元素
* 3. 支持多种事件类型（click/touchstart等）
* 4. 支持捕获阶段触发
*
* 配置参数：
* @param {Function|Object} binding.value - 回调函数或配置对象
* @param {Function} binding.value.handler - 必选，回调函数
* @param {String} [binding.value.eventType=click] - 监听的事件类型
* @param {String} [binding.value.ignoreClass] - 需要忽略的class名称
* @param {String} [binding.value.limitContainerClass] - 限定范围的容器class
* @param {Boolean} [binding.value.capture=false] - 是否使用捕获阶段
*
* 使用示例：
* 1. 基本用法：v-click-outside="callback"
    * 2. 带参数：v-click-outside:touchstart="callback"
    * 3. 带修饰符：v-click-outside.capture="callback"
    * 4. 配置对象：v-click-outside="{ handler: fn, limitContainerClass: 'wrapper' }"
    *
    * 注意事项：
* 1. 当使用limitContainerClass时，请确保容器元素存在且唯一
* 2. class名称中的特殊字符会自动转义
* 3. 同时使用arg和配置对象时，配置对象中的eventType优先级更高
*/
/*
* <div v-click-outside="{
  handler: handleClick,
  limitContainerClass: 'content-area',
  ignoreClass: 'no-trigger',
  eventType: 'mousedown'
}">
  可交互内容
</div>
* */

function escapeCss (className) {
    if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') {
        return CSS.escape(className)
    }
    return className.replace(/([^\w-])/g, '\\$1')
}

const passiveMap = new Map([
    ['touchstart', true],
    ['touchmove', true],
    ['wheel', true],
    ['mousewheel', true]
])

const clickOutside = {
    mounted (el, binding) {
        let config = {
            callback: null,
            eventType: 'click',
            ignoreClass: null,
            limitContainerClass: null,
            capture: !!binding.modifiers.capture
        }

        // 解析绑定值，支持函数或配置对象
        if (typeof binding.value === 'function') {
            config.callback = binding.value
            config.eventType = binding.arg || 'click'
        } else if (typeof binding.value === 'object') {
            config = {
                ...config,
                callback: binding.value.handler,
                eventType: binding.value.eventType || binding.arg || 'click',
                ignoreClass: binding.value.ignoreClass,
                limitContainerClass: binding.value.limitContainerClass,
                capture: binding.value.capture ?? config.capture
            }
        } else {
            console.warn('[v-click-outside] 必须提供函数或配置对象')
            return
        }

        if (typeof config.callback !== 'function') {
            console.warn('[v-click-outside] 回调函数必须为函数')
            return
        }

        // 存储配置
        el._clickOutside = config

        // 事件处理函数
        el.clickOutsideHandler = (event) => {
            const { ignoreClass, limitContainerClass, callback } =
                el._clickOutside

            // 处理忽略的class
            if (ignoreClass) {
                const escaped = escapeCss(ignoreClass)
                if (event.target.closest(`.${escaped}`)) return
            }

            // 限定容器逻辑
            if (limitContainerClass) {
                const escaped = escapeCss(limitContainerClass)
                const container = document.querySelector(`.${escaped}`)

                if (!container) {
                    console.warn(
                        `[v-click-outside] 未找到容器: ${limitContainerClass}`
                    )
                    return
                }

                const inContainer = container.contains(event.target)
                const inTarget = el.contains(event.target)

                if (inContainer && !inTarget && !event.defaultPrevented) {
                    callback(event)
                }
            } else {
                if (!el.contains(event.target) && !event.defaultPrevented) {
                    callback(event)
                }
            }
        }

        // 注册事件监听
        const passive = passiveMap.has(config.eventType)
            ? passiveMap.get(config.eventType)
            : true
        document.addEventListener(config.eventType, el.clickOutsideHandler, {
            passive,
            capture: config.capture
        })
    },

    updated (el, binding) {
        if (typeof binding.value === 'object') {
            el._clickOutside = {
                ...el._clickOutside,
                callback: binding.value.handler,
                ignoreClass: binding.value.ignoreClass,
                limitContainerClass: binding.value.limitContainerClass
            }
        } else {
            el._clickOutside.callback = binding.value
        }
    },

    unmounted (el) {
        const { eventType, capture } = el._clickOutside
        document.removeEventListener(eventType, el.clickOutsideHandler, {
            capture
        })
    }
}

export default clickOutside
